<?php

namespace App\Service;

class UploadFileService {
    private $max_size = 2097152;        // 设置上传文件的大小，此为2M 2* 1024 * 1024
    private $allow_type = [];           // 允许上传的文件扩展名
    private $absolute_save_path = '';   // 文件保存绝对路径
    private $relative_save_path = '';   // 文件保存相对路径
    private $file = '';                 // 等待上传的文件
    private $file_ext = '';             // 上传文件的扩展名
    private $file_name = '';            // 文件原名称
    private $file_size = 0;             // 文件大小
    private $file_tmp_name = '';        // 文件临时名称
    private $file_error = '';           // 文件错误码

    const MESSAGE_TYPE_SUCCESS = 1; // 返回类型：成功
    const MESSAGE_TYPE_FAILURE = 2; // 返回类型：失败

    /**
     * User: Gao
     * Date: 2020/4/1
     * Description: 文件上传处理
     * @param $absolute_save_path
     * @param $relative_save_path
     * @param $allow_type
     * @param $max_size
     * @param $file
     * @param $is_check_upload
     * @return object
     */
    public function upload ($absolute_save_path, $relative_save_path, $allow_type, $max_size, $file, $is_check_upload = true) {
        $this->absolute_save_path = $absolute_save_path;
        $this->relative_save_path = $relative_save_path;
        $this->allow_type = $allow_type;
        $this->max_size = $max_size;
        $this->file = $file;

        // 是否有文件上传
        if (!$this->file) {
            return $this->message(self::MESSAGE_TYPE_FAILURE, '未上传文件');
        }

        // 获取上传文件相关信息
        $this->file_name = $this->file['name'];
        $this->file_size = $this->file['size'];
        $this->file_error = $this->file['error'];
        $this->file_tmp_name = $this->file['tmp_name'];
        $this->file_ext = $this->getFileExtension($this->file_name);

        // 错误判断处理
        if (!empty($this->file_error)) {
            switch ($this->file_error) {
                case '1':
                    $error = '超过php.ini允许的大小';
                    break;
                case '2':
                    $error = '超过表单允许的大小';
                    break;
                case '3':
                    $error = '只有部分被上传';
                    break;
                case '4':
                    $error = '未上传文件';
                    break;
                case '6':
                    $error = '找不到临时目录';
                    break;
                case '7':
                    $error = '写文件到硬盘出错';
                    break;
                case '8':
                    $error = 'File upload stopped by extension';
                    break;
                case '999':
                default:
                    $error = '未知错误';
            }
            return $this->message(self::MESSAGE_TYPE_FAILURE, $error);
        }

        // 文件保存目录路径，不存在则创建
        if (!is_dir($this->absolute_save_path)) {
            @mkdir($this->absolute_save_path, 0777, true);
            @chmod($this->absolute_save_path, 0777);
        }
        // 检查文件名
        if (!$this->file_name) {
            return $this->message(self::MESSAGE_TYPE_FAILURE, '未上传文件');
        }
        // 检查目录
        if (is_dir($this->absolute_save_path) === false) {
            return $this->message(self::MESSAGE_TYPE_FAILURE, '上传目录不存在');
        }
        // 检查目录写权限
        if (@is_writable($this->absolute_save_path) === false) {
            return $this->message(self::MESSAGE_TYPE_FAILURE, '上传目录没有写权限');
        }
        if ($is_check_upload) {
            // 检查是否已上传
            if (@is_uploaded_file($this->file_tmp_name) === false) {
                return $this->message(self::MESSAGE_TYPE_FAILURE, '上传失败');
            }
        }
        // 检查文件大小
        if ($this->file_size > $this->max_size) {
            return $this->message(self::MESSAGE_TYPE_FAILURE, '上传文件大小超过限制，允许的大小为'.$this->fileConvertUnit($this->max_size));
        }
        // 检查扩展名
        if (in_array($this->file_ext, $this->allow_type) === false) {
            return $this->message(self::MESSAGE_TYPE_FAILURE, '上传文件不是允许的扩展，只允许 '.implode(',', $this->allow_type).' 格式');
        }
        // 创建文件夹
        $ymd = date("Ymd");
        $this->absolute_save_path .= $ymd."/";
        $this->relative_save_path .= $ymd."/";
        if (!file_exists($this->absolute_save_path)) {
            @mkdir($this->absolute_save_path, 0777, true);
        }
        // 新文件名
        $new_file_name = $this->getUniqueName().'.'.$this->file_ext;

        // 移动文件
        $absolute_file_path = $this->absolute_save_path.$new_file_name;
        $relative_file_path = $this->relative_save_path.$new_file_name;
        if ($is_check_upload) {
            if (move_uploaded_file($this->file_tmp_name, $absolute_file_path) === false) {
                return $this->message(self::MESSAGE_TYPE_FAILURE, '上传文件失败');
            }
        } else {
            copy($this->file_tmp_name, $absolute_file_path);
        }

        // 修改文件权限
        @chmod($absolute_file_path, 0644);

        return $this->message(self::MESSAGE_TYPE_SUCCESS, '文件上传成功', $this->file_name, $new_file_name, $absolute_file_path, $relative_file_path);
    }

    /**
     * User: Gao
     * Date: 2019-05-02
     * Description: 获取文件扩展名
     * @param $file_name
     * @return string
     */
    private function getFileExtension ($file_name) {
        return pathinfo($file_name, PATHINFO_EXTENSION);
    }

    /**
     * User: Gao
     * Date: 2019-05-02
     * Description: 生成新文件名
     * @return string
     */
    private function getUniqueName () {
        return md5(uniqid(microtime(true), true));
    }

    /**
     * User: Gao
     * Date: 2020/4/1
     * Description: 字节大小转换
     * @param $size
     * @return string
     */
    private function fileConvertUnit ($size) {
        $result = "";

        if ($size < 1024) {
            $result = round($size, 2).'B';
        } else if ($size < 1048576) {
            $result = round($size / 1024, 2).'K';
        } else if ($size < 1073741824) {
            $result = round($size / 1048576, 2).'M';
        } else {
            $result = round($size / 1073741824, 2).'G';
        }

        return $result;
    }

    /**
     * User: Gao
     * Date: 2020/4/1
     * Description: 返回处理结果
     * @param $type
     * @param $message
     * @param string $origin_file_name
     * @param string $new_file_name
     * @param string $absolute_file_path
     * @param string $relative_file_path
     * @return object
     */
    private function message ($type, $message, $origin_file_name = '', $new_file_name = '', $absolute_file_path = '', $relative_file_path = '') {
        $data = [
            'type'               => $type,
            'message'            => $message,
            'origin_file_name'   => $origin_file_name,
            'new_file_name'      => $new_file_name,
            'file_size'          => $this->file_size,
            'absolute_file_path' => $absolute_file_path,
            'relative_file_path' => $relative_file_path,
        ];

        return (object)$data;
    }
}